﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml.Linq;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Electrical;
using Autodesk.Revit.DB.Mechanical;
using Autodesk.Revit.DB.Plumbing;
using Autodesk.Revit.UI;
using YArchitech.LIB;

namespace YArchitech.Revit
{
	public class SearchSystem
	{
		public static bool LogEnabled
		{
			get
			{
				return Log.Enabled;
			}
			set
			{
				Log.Enabled = value;
			}
		}

		public SearchSystemFilter Filter { get; set; }

		public string ErrorMessage { get; protected set; }

		public UIDocument UIDocument { get; private set; }

		public SystemInfo SystemInfo { get; set; }

		public SearchNode RootNode { get; protected set; }

		public List<SearchNode> Nodes { get; set; }

		public SearchSystem(UIDocument uiDoc, SearchSystemFilter filter = SearchSystemFilter.ByName)
		{
			RevitUtility.CurrentUIDocument = uiDoc;
			this.UIDocument = uiDoc;
			this.Filter = filter;
			RevitUtility.LinkedDocuments = this.UIDocument.Document.GetLinkedDocuments();
		}

		protected SelectedElement PickFirstElement(string prompt)
		{
			string prompt2 = string.IsNullOrEmpty(prompt) ? "请点击要搜索的分支开始端：" : prompt;
			return this.UIDocument.PickMEPCurve(prompt2);
		}

		public bool Run(string prompt = "")
		{
			this.ErrorMessage = string.Empty;
			return this.Run(this.PickFirstElement(prompt));
		}

		public bool RunAll(SelectedElement selEnt)
		{
			this.ErrorMessage = string.Empty;
			bool result;
			try
			{
				Curve curve = (selEnt.Element.Location as LocationCurve).Curve;
				SelectedElement selectedElement = new SelectedElement();
				selectedElement.Element = selEnt.Element;
				selectedElement.SelectedPoint = curve.GetEndPoint(0);
				SelectedElement selectedElement2 = new SelectedElement();
				selectedElement2.Element = selEnt.Element;
				selectedElement2.SelectedPoint = curve.GetEndPoint(1);
				if (this.Run(selectedElement) && this.Run(selectedElement2))
				{
					result = true;
				}
				else
				{
					result = false;
				}
			}
			catch
			{
				result = false;
			}
			return result;
		}

		public bool Run(SelectedElement selectedElement)
		{
			this.ErrorMessage = string.Empty;
			return selectedElement != null && this.Run(selectedElement.Element, selectedElement.SelectedPoint);
		}

		public bool Run(Element pickedElement, XYZ pickedPoint)
		{
			this.SystemInfo = new SystemInfo(pickedElement);
			this.RootNode = new SearchNode(this, pickedElement, pickedPoint);
			return this.RunByRootNode();
		}

		public bool Run(Connector connector)
		{
			this.ErrorMessage = string.Empty;
			if (connector == null)
			{
				return false;
			}
			this.SystemInfo = new SystemInfo(connector);
			this.RootNode = new SearchNode(this, connector);
			return this.RunByRootNode();
		}

		private bool RunByRootNode()
		{
			Log.Info(new string[]
			{
				"System Name is [" + this.SystemInfo.Name + "]"
			});
			Log.Info(new string[]
			{
				"System Class Is  {0}".Format(new object[]
				{
					this.SystemInfo.SystemClass
				})
			});
			if (this.RootNode == null)
			{
				this.ErrorMessage = "没有选择实体，命令取消!";
				return false;
			}
			this.Add(this.RootNode);
			return this.DoSearch();
		}

		public SearchNode GetListRoot()
		{
			SearchNode result = null;
			foreach (SearchNode searchNode in this.Nodes)
			{
				if (searchNode.PreviousNode == null || searchNode.NextNode == null)
				{
					Element element = searchNode.Element;
					if (element is Conduit || element is CableTray || element is Pipe || element is Duct)
					{
						result = searchNode;
						break;
					}
				}
			}
			return result;
		}

		private bool DoSearch()
		{
			this.RootNode.System = this;
			bool result;
			try
			{
				this.RootNode.SearchNext();
				result = true;
			}
			catch (Exception ex)
			{
				this.ErrorMessage = "SearchSyetem DoSearch Error:  " + ex.Message;
				Log.Error(new string[]
				{
					this.ErrorMessage
				});
				result = false;
			}
			return result;
		}

		public ElementNode ConvertToSearchBranchSystem()
		{
			ElementNode elementNode = new ElementNode(this.RootNode.Element, this.RootNode.In);
			this.InitElementNode(elementNode, this.RootNode);
			this.AddNextElementNode(this.RootNode, ref elementNode);
			this.AddSubElementNodes(this.RootNode, ref elementNode);
			return elementNode;
		}

		protected bool AddNextElementNode(SearchNode previousOld, ref ElementNode previousNew)
		{
			bool result = false;
			ElementNode elementNode = null;
			if (previousOld != null)
			{
				SearchNode nextNode = previousOld.NextNode;
				if (nextNode != null)
				{
					elementNode = new ElementNode(nextNode.Element, nextNode.In);
					this.InitElementNode(elementNode, nextNode);
					previousNew.Next = elementNode;
					elementNode.Preview = previousNew;
					if (!nextNode.IsLoop)
					{
						this.AddNextElementNode(nextNode, ref elementNode);
						this.AddSubElementNodes(nextNode, ref elementNode);
					}
					result = true;
				}
			}
			return result;
		}

		protected bool AddSubElementNodes(SearchNode previousOld, ref ElementNode previousNew)
		{
			bool result = false;
			ElementNode elementNode = null;
			if (previousOld != null)
			{
				SearchNode branchNode = previousOld.BranchNode;
				if (branchNode != null)
				{
					elementNode = new ElementNode(branchNode.Element, branchNode.In);
					this.InitElementNode(elementNode, branchNode);
					previousNew.SubElementNode.Add(elementNode);
					if (!branchNode.IsLoop)
					{
						this.AddNextElementNode(branchNode, ref elementNode);
						this.AddSubElementNodes(branchNode, ref elementNode);
					}
				}
				SearchNode branch2Node = previousOld.Branch2Node;
				if (branch2Node != null)
				{
					elementNode = new ElementNode(branch2Node.Element, branch2Node.In);
					this.InitElementNode(elementNode, branch2Node);
					previousNew.SubElementNode.Add(elementNode);
					if (!branch2Node.IsLoop)
					{
						this.AddNextElementNode(branch2Node, ref elementNode);
						this.AddSubElementNodes(branch2Node, ref elementNode);
					}
				}
				if (previousOld.BranchSideNode.Count > 0)
				{
					foreach (SearchNode searchNode in previousOld.BranchSideNode)
					{
						elementNode = new ElementNode(searchNode.Element, searchNode.In);
						this.InitElementNode(elementNode, searchNode);
						previousNew.SubElementNode.Add(elementNode);
						if (!searchNode.IsLoop)
						{
							this.AddNextElementNode(searchNode, ref elementNode);
							this.AddSubElementNodes(searchNode, ref elementNode);
						}
					}
				}
				result = true;
			}
			return result;
		}

		protected void InitElementNode(ElementNode nodeNew, SearchNode nodeOld)
		{
			nodeNew.ConnectorIn = nodeOld.In;
			nodeNew.ConnectorOut = nodeOld.Out;
			if (nodeNew.SubConnectors != null)
			{
				nodeNew.SubConnectors.Clear();
				if (nodeOld.Side != null)
				{
					nodeNew.SubConnectors.Add(nodeOld.Side);
				}
				if (nodeOld.Side2 != null)
				{
					nodeNew.SubConnectors.Add(nodeOld.Side2);
				}
				if (nodeOld.SideOther.Count > 0)
				{
					foreach (Connector item in nodeOld.SideOther)
					{
						nodeNew.SubConnectors.Add(item);
					}
				}
			}
		}

		public bool Add(SearchNode node)
		{
			if (node == null)
			{
				return false;
			}
			if (this.Nodes == null)
			{
				this.Nodes = new List<SearchNode>
				{
					node
				};
				return true;
			}
			if (this.Contains(node))
			{
				Log.Warning(new string[]
				{
					"Contains Id " + node.Id.IntegerValue.ToString()
				});
				return false;
			}
			this.Nodes.Add(node);
			return true;
		}

		public bool Contains(SearchNode node)
		{
			return this.Nodes != null && this.Nodes.Any((SearchNode n) => n.Id == node.Id);
		}

		public SearchNode GetNode(Element element)
		{
			return this.GetNode(element.Id);
		}

		public SearchNode GetNode(ElementId elementId)
		{
			if (this.Nodes != null)
			{
				return this.Nodes.Find((SearchNode node) => node.Element.Id == elementId);
			}
			return null;
		}

		public bool IsLoop()
		{
			if (this.Nodes != null)
			{
				return this.Nodes.Any((SearchNode n) => n.IsLoop);
			}
			return false;
		}

		public IEnumerable<SearchNode> GetLoopNodes()
		{
			if (this.Nodes != null)
			{
				return from n in this.Nodes
				where n.IsLoop
				select n;
			}
			return Enumerable.Empty<SearchNode>();
		}

		public IEnumerable<Element> GetLoopElements()
		{
			return from n in this.GetLoopNodes()
			select n.Element;
		}

		public bool Save()
		{
			string text = Product.WorkingLocation;
			if (string.IsNullOrEmpty(text))
			{
				text = Environment.ExpandEnvironmentVariables("%appdata%\\YArchitech\\HYRME2016");
			}
			string fileName = Path.Combine(text, string.Format("log\\{0}\\{1}_SearchSystemResult.xml", Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location), DateTime.Now.ToString("yyyy-MM-dd")));
			return this.Save(fileName);
		}

		public bool Save(string fileName)
		{
			Directory.CreateDirectory(Path.GetDirectoryName(fileName));
			XElement xelement = new XElement("SystemSearch");
			this.SystemInfo.Save(xelement);
			xelement.SetAttributeValue("SearchFilter", this.Filter);
			xelement.Add(this.RootNode.ToXElement());
			xelement.Save(fileName);
			return true;
		}
	}
}
